#include <stdlib.h>

#include "lib.h"
#include "ThreeCardRummySQL.h"
#include "ThreeCardRummyGame.h"
#include "tcpsaved.h"
#include "odbc.h"
#include "missinggamenums.h"
#include "opts.h"
#include "ThreeCardRummyHand.h"
#include "stdncerr.h"
#include "log.h"
#include "memory.h"



///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummySQL::ThreeCardRummySQL(TrackSQL *pTracker, TInfo *pInfo)
///
/// @param  pTracker    pointer to this server's tracker object...used to get connections to the database.
/// @param  pInfo       object that holds info on the players session...created during the login process.
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
ThreeCardRummySQL::ThreeCardRummySQL(TrackSQL *pTracker, TInfo *pInfo)
                    : TDBInterface(pTracker, pInfo)
{
    m_nPlayerScore = 0;
    m_nDealerScore = 0;

#ifdef USE_MISSINGGAMENUMS
    //missing game nums deleted in TrackSQL::~TrackSQL
    missinggamenums = new MissingGameNums(this, "pub_getTableIdentityTriCardRummy");
#endif
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8 ThreeCardRummySQL::trackNewGame(int32 nBet, int32 nSideBet, uint8 nAntePayTable, 
///                                               int8 nSidePayTable, int32 compFactor, int32* pnBalance)
///
/// Creates database records for a new Rummy game.
///
/// @param  Bet             (in)  the ante bet that the user made
/// @param  sideBet         (in)  the bonus bet that the user made
/// @param  nAntePayTable   (in)  which paytable setting the casino is using for payouts on ante bet
/// @param  nSideBetPayTable(in)  which paytable setting the casino is using for payouts on the bonus bet
/// @param  compFactor      (in)
/// @param  pnBalance       (out) user's balance after making this bet.
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummySQL::trackNewGame(int32 nBet, int32 nSideBet, uint8 nAntePayTable, 
                                int8 nSideBetPayTable, int32 compFactor, int32* pnBalance)
{
    int32 result = TrackSQL::ExecFailed;
    int32 comp;

    if (m_pTracker && m_pInfo)
    {
        m_pTracker->lock();

        result = createGame(ServerOpts::casinoId, m_pInfo->pid, m_pInfo->forMoney, 
                            m_pInfo->sessionId, m_pInfo->forMoney ? compFactor : 0, 
                            m_pInfo->gid, nBet, nSideBet, nAntePayTable, 
                            nSideBetPayTable, pnBalance, &comp);

        m_pTracker->unlock();
    }

    return (bool8)(result == TrackSQL::NoError);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8 ThreeCardRummySQL::trackDeal(TGame *pGame)
///
/// Records information in the database about the cards just dealt and ties that info to the current game.
///
/// @param  pGame       (in)  the game object that we're saving information about.
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummySQL::trackDeal(TGame *pGame)
{    
    int32 result = TrackSQL::ExecFailed;

    if (m_pTracker && m_pInfo)
    {
        m_pTracker->lock();

        // kharmon.  10-17-06.  #8226.  Save the deck seed here, not the actual cards.
        ThreeCardRummyGame * pTCPGame = (ThreeCardRummyGame *) pGame;

        ThreeCardRummyHand* pPlayer = pTCPGame->getPlayer();

        result = deal(m_pInfo->pid, m_pInfo->gid, pTCPGame->getDeckSeed(),
                      pPlayer->getCount(), pPlayer->getCards());

        m_pTracker->unlock();
    }
    return (bool8)(result == TrackSQL::NoError);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8 ThreeCardRummySQL::trackGameDone(bool8 bRaised, int32 nPayAnte, int32 nPayRaise, 
///                                                   int32 nPaySideBet, int32 nOutcome, int32 nBalAdjust, 
///                                                int32 *pnBalance)
///
/// Records information in the database about the outcome of the game that the user just played.
///
/// @param  bRaised          (in)  Did the user raise or fold?
/// @param  nPayAnte         (in)  How much did the user win OR lose off of the ante
/// @param  nPayRaise        (in)  How much did the user win OR lose off of the raise.
/// @param  nPaySideBet      (in)  How much (if anything) did the user win on the sidebet?
/// @param  nOutcome         (in)  Who won?  (see OutcomePossibilities enum for details)
/// @param  nBalAdjust       (in)  Total amount won or lost by the user.
/// @param  pnBalance        (out) user's balance after this game.
/// @param  pGame            (in)  the game object that we're saving information about.
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummySQL::trackGameDone(bool8 bRaised, int32 nPayAnte, int32 nPayRaise, int32 nPaySideBet,
                                 int32 nOutcome, int32 nBalAdjust, int32 *pnBalance, TGame *pGame)
{
    int32 result = TrackSQL::ExecFailed;
    int32 comp;

    if (m_pTracker && m_pInfo)
    {
        m_pTracker->lock();

        // kharmon.  10-17-06.  #8226.  Save the cards for this game here, now that it is complete.
        ThreeCardRummyGame * pTCPGame = (ThreeCardRummyGame *) pGame;

        ThreeCardRummyHand* pDealer = pTCPGame->getDealer();
        ThreeCardRummyHand* pPlayer = pTCPGame->getPlayer();

        result = gameDone(ServerOpts::casinoId, m_pInfo->pid, m_pInfo->forMoney, 
                          m_pInfo->gid, bRaised, nPayAnte, nPayRaise, nPaySideBet,
                          nOutcome, nBalAdjust, pnBalance, &comp, 
                          pDealer->getCount(), pDealer->getCards(),
                          pPlayer->getCount(), pPlayer->getCards());

        m_pTracker->unlock();
    }

    return (bool8)(result == TrackSQL::NoError);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn  bool8 ThreeCardRummySQL::trackHasSavedGame(ThreeCardConnection *pConn, TGame ** ppGame, int32 * err)
///
/// Checks for unfinished Three Card Rummy games that this player might have laying around, and if one is
/// found returns the state information to the caller so that the game can be reincarnated.
///
/// @param  pConn       (in)  This method may create a new Rummy game, and this is needed if it does.
/// @param  ppGame      (out) Pointer to new Holdem game, IF one is created.
/// @param  err         (out) If something goes wrong, the value returned should give some idea of what
///
/// @return bool8       true if a game was re-created successfully.  false otherwise.
///
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummySQL::trackHasSavedGame(ThreeCardConnection *pConn, TGame ** ppGame, int32 * err)
{
    ThreeCardSavedGame sg;
    bool8 exists;
    int32 result;

    if (m_pTracker && m_pInfo)
    {
        m_pTracker->lock();

        result = hasSavedGame(m_pInfo->pid, m_pInfo->forMoney,
                              m_pInfo->gid, &sg, &exists);

        m_pTracker->unlock();

        if (result == TrackSQL::NoError && exists)
        {
            *ppGame = new ThreeCardRummyGame(pConn, &sg);
            if (*ppGame && (*ppGame)->isGameOK())
            {
                return true;
            }
            else
            {
                delete *ppGame;
                *ppGame = 0;

                *err = LOGINERR_CONTACTCUSTSERVICE;
                return false;
            }
        }
        else
        {
            memset(m_pInfo->gid, 0xff, sizeof(ThreeCardGameID));

            if (result == TrackSQL::NoError)
            {
                *err = 0;
                return false;            // all ok, no saved game
            }
        }
    }
    *err = LOGINERR_CONTACTCUSTSERVICE;
    return false;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8 ThreeCardRummySQL::trackGetOptions(uint8 *pnAntePayTable, uint8 *pnSidePayTable)
///
/// Returns the specific setup options for Vegas Three Card Rummy.  Not really needed at this time, since
/// Rummy doesn't have any configurable options, just in case new paytables get added in the future, we'll
/// go through the motions.
///
/// @param  pnAntePayTable  (out) Which set of payout numbers should be used for normal wins?
/// @param  pnSidePayTable  (out) Which set of payout numbers should be used when the user wins the bonus bet?
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8 
ThreeCardRummySQL::trackGetOptions(uint8 *pnAntePayTable, uint8 *pnSidePayTable)
{
    int32 result = TrackSQL::ExecFailed;

    if (m_pTracker)
    {
        m_pTracker->lock();
        result = getOptions(pnAntePayTable, pnSidePayTable);
        m_pTracker->unlock();
    }

    if (result == TrackSQL::NoError)
    {

        if (*pnAntePayTable < ThreeCardRummyGame::AB_PAYFREQ_LOW || 
            *pnAntePayTable >= ThreeCardRummyGame::MAX_AB_PAYFREQ)
        {
            *pnAntePayTable = 0;
        }

        if (*pnSidePayTable <  ThreeCardRummyGame::SB_PAYFREQ_LOW || 
            *pnSidePayTable >= ThreeCardRummyGame::MAX_SB_PAYFREQ)
        {
            *pnSidePayTable = 0;
        }

         return true;
    }
    else
    {
        Log::msgError("Could not load Vegas Three Card Rummy options.");
        return false;
    }
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     int32 ThreeCardRummySQL::createGame(int32 casinoId, PID pid, bool8 forMoney, SESSIONID sessionId, 
///                                                int32 compFactor, ThreeCardGameID gid, int32 anteBet, 
///                                                int32 nSideBet, uint8 anteBonusPayTable, 
///                                                uint8 nSideBetPayTable, int32* balance, int32* comp)
///
/// Creates database records for a new Vegas Three Card Rummy game.
///
/// @param  casinoID        (in)
/// @param  pid             (in)  unique identifier for the current player
/// @param  forMoney        (in)  is this session for fun or for money?
/// @param  sessionId       (in)  unique identifier for the player's current session
/// @param  compFactor      (in)
/// @param  gid             (out) unique identifier for this current game round that we're about to create
/// @param  anteBet         (in)  The amount of the player's initial bet
/// @param  nSideBet        (in)  The amount of the player's bonus bet
/// @param  antePayTable    (in)  Which set of payout numbers are being used used for normal wins?
/// @param  sidePayTable    (in)  Which set of payout numbers are being used when the user wins the bonus bet?
/// @param  balance         (out) user's balance after making these bets.
/// @param  comp            (out) comp points generated by this round
///
/// @return int32            TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
int32 
ThreeCardRummySQL::createGame(int32 casinoId, PID pid, bool8 forMoney, SESSIONID sessionId, int32 compFactor,
                              ThreeCardGameID gid, int32 anteBet, int32 nSideBet, uint8 anteBonusPayTable, 
                              uint8 nSideBetPayTable, int32* balance, int32* comp)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;

    conn = m_pTracker->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_CreateGameThreeCardRummy ");
        query->addParam(casinoId);
        query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));
        query->addParam(sessionId, ODBCQuery::NOQUOTES);
        query->addParam(compFactor);
        query->addParam(anteBet, ODBCQuery::MONEY);
        query->addParam(nSideBet, ODBCQuery::MONEY);
        query->addParam(anteBonusPayTable);
        query->addParam(nSideBetPayTable);

        if (query->exec())
        {
            while (query->getRow())
            {
                ok =
                    query->getData(1, gid, TCP_GAMEID_SIZE) &&
                    query->getData(2, balance, ODBCQuery::MONEY) &&
                    query->getData(3, comp);
            }
        }
        delete query;
    }
    delete conn;

#ifdef USE_MISSINGGAMENUMS
    if (ok)
    {
        int32 gamenum = atol(gid);
        if (missinggamenums)
            missinggamenums->setGameNum(gamenum);
    }
#endif

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     int32 ThreeCardRummySQL::deal(PID pid, ThreeCardGameID gid, int32 nDeckSeed)
///
/// Records information in the database about the cards just dealt and ties that info to the current game.
///
/// @param  pid             (in)  unique id of current player
/// @param  gid             (in)  unique id of current hand for this hand of Hold'em
/// @param  nDeckSeed       (in)  seed that can be used to re-generate the cards dealt on this hand.
/// @param  numPlayer       (in)  number of player cards
/// @param  player          (in)  player cards
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
int32
ThreeCardRummySQL::deal(PID pid, ThreeCardGameID gid, int32 nDeckSeed, int32 numPlayer, uint8* player)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;

    conn = m_pTracker->getConn();
    if (!conn)    
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_ThreeCardRummyDeal ");
        query->addParam(pid);
        query->addParam(gid, ODBCQuery::NOQUOTES);
        query->addParam(nDeckSeed);
        query->addParam(player, numPlayer, ODBCQuery::BINARY);

        if (query->exec())
        {
            ok = true;

            // Clear out recordset
            while (query->getRow());
        }
        delete query;
    }
    delete conn;

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn int32 ThreeCardRummySQL::gameDone(int32 casinoId, PID pid, bool8 forMoney, ThreeCardGameID gid, char raised,
///                                     int32 payAnte, int32 payRaise, int32 nPaySideBet, int32 outcome,
///                                     int32 balanceAdjust, int32* balance, int32* comp, int32 numDealer, 
///                                     uint8* dealer, int32 numPlayer, uint8* player)
///
///
/// Records information in the database about the outcome of the game that the user just played.
///
/// @param  casinoID        (in)
/// @param  pid             (in)  unique identifier for the current player
/// @param  forMoney        (in)  is this session for fun or for money?
/// @param  gid             (in)  unique identifier for this current hand of Rummy
/// @param  raised          (in)  Did the user raise or fold?
/// @param  payAnte         (in)  How much did the user win OR lose off of the ante
/// @param  payRaise        (in)  How much did the user win OR lose off of the raise.
/// @param  payMultiBank    (in)  How much (if anything) did the user win on the "bonus bet"?
/// @param  outcome         (in)  Who won?  (see OutcomePossibilities enum for details)
/// @param  balanceAdjust   (in)  Total amount won or lost by the user.
/// @param  balance         (out) user's balance after this game.
/// @param  comp            (out) number of comp points generated by this round
/// @param  numDealer       (in)  number of dealer cards
/// @param  dealer          (in)  dealer cards
/// @param  numPlayer       (in)  number of player cards
/// @param  player          (in)  player cards
///
/// @return int32            TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int32
ThreeCardRummySQL::gameDone(int32 casinoId, PID pid, bool8 forMoney, ThreeCardGameID gid, char raised,
                            int32 payAnte, int32 payRaise, int32 nPaySideBet, int32 outcome,
                            int32 balanceAdjust, int32* balance, int32* comp, int32 numDealer, 
                            uint8* dealer, int32 numPlayer, uint8* player)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;
    char raised_str[2] = { raised, 0 };

    conn = m_pTracker->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_ThreeCardRummyGameDone ");
        query->addParam(casinoId);
        query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));
        query->addParam(gid, ODBCQuery::NOQUOTES);
        query->addParam(raised_str);
        query->addParam(payAnte, ODBCQuery::MONEY);
        query->addParam(payRaise, ODBCQuery::MONEY);
        query->addParam(nPaySideBet, ODBCQuery::MONEY);
        query->addParam((uint8) m_nDealerScore);
        query->addParam((uint8) m_nPlayerScore);
        query->addParam((uint8)(outcome & 0xffL), ODBCQuery::BINARY);
        query->addParam(balanceAdjust, ODBCQuery::MONEY);
        query->addParam(dealer, numDealer, ODBCQuery::BINARY);
        query->addParam(player, numPlayer, ODBCQuery::BINARY);

        if (query->exec())
        {
            while (query->getRow())
            {
                ok = query->getData(1, balance, ODBCQuery::MONEY) &&
                    query->getData(2, comp);
            }
        }
        delete query;
    }
    delete conn;

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn ThreeCardRummySQL::hasSavedGame(PID pid, bool8 forMoney, ThreeCardGameID gid,
///                                     ThreeCardSavedGame* dest, bool8* exists)
/// 
/// Checks for unfinished Rummy games that this player might have laying around and if one is
/// found, returns the state information to the caller so that the game can be reincarnated.
///
/// @param  pid             (in)  unique identifier for the current player
/// @param  forMoney        (in)  is this session for fun or for money?
/// @param  gid             (in)  unique identifier for this current Hold'em game
/// @param  dest            (out) if a saved game is found, it's state info is returned in here.
/// @param  exists          (out) true if we found a saved game, false if not.
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
int32
ThreeCardRummySQL::hasSavedGame(PID pid, bool8 forMoney, ThreeCardGameID gid,
                                ThreeCardSavedGame* dest, bool8* exists)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;

    *exists = false;

    conn = m_pTracker->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_ThreeCardRummyGetSavedGame ");
        query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));

        if (query->exec())
        {
            int32 hasSaved = 0;

            ok = true;

            while (query->getRow())
            {
                if (query->getData(1, &hasSaved) && hasSaved!=0)
                {
                    *exists = true;

                    if(hasSaved==1)
                    {
                        // kharmon.  10-17-06.  #8226.  This is an older saved game, with no deckseed.  Instead, load the actual cards.
                        dest->bHasDeckSeed = false;

                        query->getData(2, gid, TCP_GAMEID_SIZE);
                        query->getData(3, &dest->anteBet, ODBCQuery::MONEY);
                        query->getData(4, &dest->sideBet, ODBCQuery::MONEY);
                        query->getData(5, dest->dealerCards,
                            ThreeCardSavedGame::MAXCARDS, &dest->numDealerCards);
                        query->getData(6, dest->playerCards,
                            ThreeCardSavedGame::MAXCARDS, &dest->numPlayerCards);
                        query->getData(7, &dest->anteBonusPayTable);
                        query->getData(8, &dest->sideBetPayTable);
                    }
                    else if(hasSaved==2)
                    {
                        // kharmon.  10-17-06.  #8226.  This is a newer saved game, with a deckseed.
                        dest->bHasDeckSeed = true;

                        query->getData(2, gid, TCP_GAMEID_SIZE);
                        query->getData(3, &dest->anteBet, ODBCQuery::MONEY);
                        query->getData(4, &dest->sideBet, ODBCQuery::MONEY);
                        query->getData(5, &dest->nDeckSeed);
                        query->getData(6, &dest->anteBonusPayTable);
                        query->getData(7, &dest->sideBetPayTable);
                    }
                }
            }
        }
        delete query;
    }
    delete conn;

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn   int32 ThreeCardRummySQL::getOptions(uint8* pnAnteBonusPayTable, uint8* pnSideBetPayTable)
///
/// Returns the specific setup options that the casino has selected for Vegas Three Card Rummy.
///
/// @param  pnAntePayTable  (out) Which set of payout numbers should be used for normal wins?
/// @param  pnSidePayTable  (out) Which set of payout numbers should be used when the user wins the bonus bet?
///
/// @return int32           TrackSQL::NoError on success, TrackSQL::ExecFailed on failure
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
int32
ThreeCardRummySQL::getOptions(uint8* pnAnteBonusPayTable, uint8* pnSideBetPayTable)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;

    conn = m_pTracker->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_ThreeCardRummyGetOptions ");

        if (query->exec())
        {
            ok = false;

            while (query->getRow())
            {
                ok = query->getData(1, pnAnteBonusPayTable) &&
                     query->getData(2, pnSideBetPayTable);
            }
        }
        delete query;
    }
    delete conn;

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn   void ThreeCardRummySQL::setScores(int8 nPlayerScore, int8 nDealerScore)
///
/// Returns the specific setup options that the casino has selected for Vegas Three Card Rummy.
///
/// @param  nPlayerScore    (in) point value of player's cards
/// @param  nDealerScore    (in) point value of dealer's cards
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreeCardRummySQL::setScores(int8 nPlayerScore, int8 nDealerScore)
{
    m_nPlayerScore = nPlayerScore;
    m_nDealerScore = nDealerScore;
}

